package edu.washington.cs.sounddetector.server.api;

import java.util.HashMap;
import java.util.Map;

import edu.washington.cs.sounddetector.server.api.exception.SoundDetectorInternalErrorException;
import edu.washington.cs.sounddetector.server.api.method.Classify;
import edu.washington.cs.sounddetector.server.api.method.GetOldNotifications;
import edu.washington.cs.sounddetector.server.api.method.GetPendingNotifications;
import edu.washington.cs.sounddetector.server.api.method.GetSound;
import edu.washington.cs.sounddetector.server.api.method.GetSounds;
import edu.washington.cs.sounddetector.server.api.method.RegisterReceiverPhone;
import edu.washington.cs.sounddetector.server.api.method.RenameDetector;
import edu.washington.cs.sounddetector.server.api.method.SetSoundClassProperties;
import edu.washington.cs.sounddetector.server.api.method.Train;
import edu.washington.cs.sounddetector.server.api.method.UnregisterReceiverPhone;
import edu.washington.cs.sounddetector.server.api.method.Untrain;
import edu.washington.cs.sounddetector.server.api.methodlogic.DeleteLogic;
import edu.washington.cs.sounddetector.server.api.methodlogic.ReadLogic;
import edu.washington.cs.sounddetector.server.api.methodlogic.WriteLogic;

/**
 * <p>
 * MethodLogic provides a convenience method for executing Method objects.
 * </p>
 * 
 * @author the.dylan.price@gmail.com
 */
public class MethodLogic
{

    private Map<Class<? extends Method>, MethodType> methodClassMap;
    private ReadLogic rl;
    private WriteLogic wl;
    private DeleteLogic dl;

    public MethodLogic(ReadLogic rl, WriteLogic wl, DeleteLogic dl)
    {
        this.rl = rl;
        this.wl = wl;
        this.dl = dl;
        initializeMethodClassMap();
    }

    private void initializeMethodClassMap()
    {
        methodClassMap = new HashMap<Class<? extends Method>, MethodType>();

        methodClassMap.put(RegisterReceiverPhone.class, MethodType.REGISTER_RECEIVER_PHONE);
        methodClassMap.put(UnregisterReceiverPhone.class, MethodType.UNREGISTER_RECEIVER_PHONE);
        methodClassMap.put(Train.class, MethodType.TRAIN);
        methodClassMap.put(Untrain.class, MethodType.UNTRAIN);
        methodClassMap.put(Classify.class, MethodType.CLASSIFY);
        methodClassMap.put(GetPendingNotifications.class, MethodType.GET_PENDING_NOTIFICATIONS);
        methodClassMap.put(GetOldNotifications.class, MethodType.GET_OLD_NOTIFICATIONS);
        methodClassMap.put(GetSound.class, MethodType.GET_SOUND);
        methodClassMap.put(GetSounds.class, MethodType.GET_SOUNDS);
        methodClassMap.put(SetSoundClassProperties.class, MethodType.SET_SOUND_CLASS_PROPERTIES);
        methodClassMap.put(RenameDetector.class, MethodType.RENAME_DETECTOR);
    }

    /**
     * Executes the given method and returns the result.
     * 
     * @param method
     *            the method to execute
     * @return the MethodResult corresponding to the result of execution
     * @throws SoundDetectorInternalErrorException
     *             if an internal server error occurred
     */
    public MethodResult<? extends Method> execute(Method method)
            throws SoundDetectorInternalErrorException
    {
        Class<?> methodClass = method.getClass();
        MethodType methodType = methodClassMap.get(methodClass);

        if (methodType == null)
            throw new IllegalArgumentException("No such method: " + method);

        switch (methodType)
        {
        case REGISTER_RECEIVER_PHONE:
            return wl.registerReceiverPhone((RegisterReceiverPhone) method);
        case UNREGISTER_RECEIVER_PHONE:
            return dl.unregisterReceiverPhone((UnregisterReceiverPhone) method);
        case TRAIN:
            return wl.train((Train) method);
        case UNTRAIN:
            return dl.untrain((Untrain) method);
        case CLASSIFY:
            return wl.classify((Classify) method);
        case GET_PENDING_NOTIFICATIONS:
            return wl.getPendingNotifications((GetPendingNotifications) method);
        case GET_OLD_NOTIFICATIONS:
            return rl.getOldNotifications((GetOldNotifications) method);
        case GET_SOUND:
            return rl.getSound((GetSound) method);
        case GET_SOUNDS:
            return rl.getSounds((GetSounds) method);
        case SET_SOUND_CLASS_PROPERTIES:
            return wl.setSoundClassProperties((SetSoundClassProperties) method);
        case RENAME_DETECTOR:
            return wl.renameDetector((RenameDetector) method);
        default:
            throw new IllegalArgumentException("No such method: " + method);
        }
    }

    /**
     * There should be one MethodType for each implementation of the Method interface. This is just
     * to support switching in {@link MethodLogic#execute(Method)}.
     */
    private enum MethodType
    {
        REGISTER_RECEIVER_PHONE,
        UNREGISTER_RECEIVER_PHONE,
        TRAIN,
        UNTRAIN,
        CLASSIFY,
        GET_PENDING_NOTIFICATIONS,
        GET_OLD_NOTIFICATIONS,
        GET_SOUND,
        GET_SOUNDS,
        SET_SOUND_CLASS_PROPERTIES,
        RENAME_DETECTOR,
    }
}
